package com.xxx.springboot.util;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * ThreadLocal工具类
 * 
 * ThreadLocal是线程内的全局上下文。就是在单个线程中，方法之间共享的内存，每个方法都可以从该上下文中获取值和修改值。
	实际案例：
		在调用api时都会传一个token参数，通常会写一个拦截器来校验token是否合法，我们可以通过token找到对应的用户信息(User)，如果token合法，然后将用户信息存储到ThreadLocal中，这样无论是在controller、service、dao的哪一层都能访问到该用户的信息。作用类似于Web中的request作用域。
		传统方式我们要在方法中访问某个变量，可以通过传参的形式往方法中传参，如果多个方法都要使用那么每个方法都要传参；如果使用ThreadLocal所有方法就不需要传该参数了，每个方法都可以通过ThreadLocal来访问该值。
		ThreadLocalUtil.set("key", value); 保存值
		T value = ThreadLocalUtil.get("key"); 获取值
 */
public class ThreadLocalUtil<T> {
	private static final ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal<Map<String, Object>>() {
		@Override
		protected Map<String, Object> initialValue() {
			return new HashMap<>(4);
		}
	};

	public static Map<String, Object> getThreadLocal() {
		return threadLocal.get();
	}

	public static <T> T get(String key) {
		Map map = (Map) threadLocal.get();
		return (T) map.get(key);
	}

	public static <T> T get(String key, T defaultValue) {
		Map map = (Map) threadLocal.get();
		return (T) map.get(key) == null ? defaultValue : (T) map.get(key);
	}

	public static void set(String key, Object value) {
		Map map = (Map) threadLocal.get();
		map.put(key, value);
	}

	public static void set(Map<String, Object> keyValueMap) {
		Map map = (Map) threadLocal.get();
		map.putAll(keyValueMap);
	}

	public static void remove() {
		threadLocal.remove();
	}

	public static <T> Map<String, T> fetchVarsByPrefix(String prefix) {
		Map<String, T> vars = new HashMap<>();
		if (prefix == null) {
			return vars;
		}
		Map map = (Map) threadLocal.get();
		Set<Map.Entry> set = map.entrySet();

		for (Map.Entry entry : set) {
			Object key = entry.getKey();
			if (key instanceof String) {
				if (((String) key).startsWith(prefix)) {
					vars.put((String) key, (T) entry.getValue());
				}
			}
		}
		return vars;
	}

	public static <T> T remove(String key) {
		Map map = (Map) threadLocal.get();
		return (T) map.remove(key);
	}

	public static void clear(String prefix) {
		if (prefix == null) {
			return;
		}
		Map map = (Map) threadLocal.get();
		Set<Map.Entry> set = map.entrySet();
		List<String> removeKeys = new ArrayList<>();

		for (Map.Entry entry : set) {
			Object key = entry.getKey();
			if (key instanceof String) {
				if (((String) key).startsWith(prefix)) {
					removeKeys.add((String) key);
				}
			}
		}
		for (String key : removeKeys) {
			map.remove(key);
		}
	}
}
